package com.sg;

/**
 * <p>
 * 编码转换工具类.如:BCD和HEX
 * </p>
 */
public class Util {

    protected static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

    protected static final char[] BINARY = new char[]{'0', '1'};

    private Util() {

    }

    // 传入参数为只有01的字符串
    public static byte[] binary(String binaryStr) {
        // 长度不是8倍数的话，无法知道在左边或右边补零，会引起歧义，导致结果不正确
        if (binaryStr.length() % 8 != 0) {
            throw new IllegalArgumentException("传入的参数长度必须是8的倍数");
        }
        StringBuffer accum = new StringBuffer();
        for (int i = 0; i < binaryStr.length(); i += 4) {
            String temp = binaryStr.substring(i, i + 4);
            int value = 0;
            for (int j = 0; j < 4; j++) {
                if (temp.charAt(j) == '1') {
                    value += Math.pow(2, 3 - j);// 计算值
                }
            }
            accum.append(HEX[value]);
        }
        return bcd(accum.toString());
    }

    public static String hex(byte[] bts, int offset, int length) {//
        if (offset < 0 || length < 0 || bts.length < offset + length - 1) {
            throw new IllegalArgumentException("参数非法：offset:" + offset + ",length:" + length + ",字节长度：" + bts.length);
        }
        byte[] returnBt = new byte[length];
        System.arraycopy(returnBt, 0, bts, offset, length);
        return hex(returnBt);
    }

    /**
     * 将byte数组转化为String类型的十六进制编码格式 本方法实现的思路是： 1）每位byte数组转换为2位的十六进制数
     * 2）将字节左移4位取得高四位字节数值，获取对应的char类型数组编码 3）将字节与0x0F按位与，从而获取第四位的字节，同样获取编码
     */
    public static String hex(byte[] bParam) {
        StringBuilder accum = new StringBuilder();
        for (byte bt : bParam) {
            accum.append(HEX[bt >> 4 & 0x0F]);// &0x0F的目的是为了转换负数
            accum.append(HEX[bt & 0x0F]);
        }
        return accum.toString();
    }

    public static byte[] bcd(int code, int len) {
        return Util.bcd(String.valueOf(code), len);
    }

    public static byte[] lllbcd(int code, int len) {
        return Util.lllbcd(String.valueOf(code), len);
    }

    public static String binary(byte[] bts) {
        StringBuffer accum = new StringBuffer();
        for (byte bt : bts) {
            accum.append(binary(bt));
        }
        return accum.toString();
    }

    // 本方法修改于Integer.toBinaryString
    // 参数的每个字节都会转化为8位2进制字符串，如1会转换为00000001
    private static String binary(byte bt) {
        int num = bt & 0xFF;
        char[] arrayOfChar = new char[8];
        int i = 8;
        for (int times = 0; times < 8; times++) {
            arrayOfChar[(--i)] = BINARY[(num & 0x01)];
            num >>>= 1;
        }
        return new String(arrayOfChar);
    }

    /**
     * BCD编码(8421码)为一个4位表示一个10进制数，即每个字节表示两个数
     * 本方法中的code为10进制数（本方法支持16进制数编码，每两位编为1字节）
     */
    public static byte[] bcd(String code) {
        // 控制byte数组的大小
        int len = code.length() % 2 == 0 ? code.length() / 2 : code.length() / 2 + 1;
        return bcd(code, len);
    }

    /**
     * @param code
     * @param length
     * @return
     */
    public static byte[] bcd(String code, int length) {
        if (length < 0) {
            throw new IllegalArgumentException("参数length不能小于0,length:" + length);
        } else if (length == 0) {
            return new byte[0];
        }
        byte[] bt = new byte[length];
        // 指示当前位置
        int point = 0;
        if (code.length() < 2 * length) {
            code = addBlankLeft(code, 2 * length - code.length(), "0");
        }

        System.out.println("code:" + code);

        // 每两位合并为一个字节
        for (; point < code.length(); point += 2) {
            // (point+1)/2计算当前指向的值
            // Character.digit将对应的Char转为数字，如'8'-> 8
            // <<4左移四位：即为→_→（右边）的数字让开位置
            bt[(point + 1) / 2] = (byte) (Character.digit(code.charAt(point), 16) << 4 | Character.digit(code.charAt(point + 1), 16));
        }

        return bt;
    }


    public static byte[] lllbcd(String code, int length) {
        if (length < 0) {
            throw new IllegalArgumentException("参数length不能小于0,length:" + length);
        } else if (length == 0) {
            return new byte[0];
        }
        byte[] bt = new byte[length];
        // 指示当前位置
        if (code.length() < length) {
            code = addBlankLeft(code, length - code.length(), "0");
        }

        bt = code.getBytes();
        ;

        return bt;
    }

    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    public static String addBlankLeft(String origStr, int length, String fill) {
        if (length <= 0) {
            return origStr;
        }
        StringBuffer accum = new StringBuffer();
        for (int i = 0; i < length; i++) {
            accum.append(fill);
        }
        accum.append(origStr);
        return accum.toString();
    }

    public static String addBlankRight(String origStr, int length, String fill) {
        if (length <= 0) {
            return origStr;
        }
        StringBuffer accum = new StringBuffer(origStr);
        for (int i = 0; i < length; i++) {
            accum.append(fill);
        }
        return accum.toString();
    }

    public static String hexStr2Str(String hexStr) {
        String str = "0123456789ABCDEF";
        char[] hexs = hexStr.toCharArray();
        byte[] bytes = new byte[hexStr.length() / 2];
        int n;

        for (int i = 0; i < bytes.length; i++) {
            n = str.indexOf(hexs[2 * i]) * 16;
            n += str.indexOf(hexs[2 * i + 1]);
            bytes[i] = (byte) (n & 0xff);
        }
        return new String(bytes);
    }

    public static byte[] subBytes(byte[] src, int begin, int count) {
        byte[] bs = new byte[count];
        for (int i = begin; i < begin + count; i++)
            bs[i - begin] = src[i];
        return bs;
    }

    /**
     * Convert hex string to byte[]
     *
     * @param hexString the hex string
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
}
